/**
 * Provides a class that represents named elements in Java programs.
 */

import CompilationUnit
import semmle.code.Location
import Javadoc

/** A program element that has a name. */
class Element extends @element, Top {
  /** Holds if this element has the specified `name`. */
  predicate hasName(string name) { hasName(this, name) }

  /** Gets the name of this element. */
  string getName() { this.hasName(result) }

  /**
   * Holds if this element transitively contains the specified element `e`.
   */
  predicate contains(Element e) { this.hasChildElement+(e) }

  /**
   * Holds if this element is the immediate parent of the specified element `e`.
   *
   * It is usually preferable to use more specific predicates such as
   * `getEnclosingCallable()`, `getDeclaringType()` and/or `getEnclosingType()`
   * instead of this general predicate.
   */
  predicate hasChildElement(Element e) { hasChildElement(this, e) }

  /**
   * Holds if this element pertains to a source file.
   *
   * Elements pertaining to source files may include generated elements
   * not visible in source code, such as implicit default constructors.
   */
  predicate fromSource() { this.getCompilationUnit().isSourceFile() }

  /** Gets the compilation unit that this element belongs to. */
  CompilationUnit getCompilationUnit() { result = this.getFile() }

  /** Cast this element to a `Documentable`. */
  Documentable getDoc() { result = this }

  /** Holds if this is an auxiliary program element generated by the compiler. */
  predicate isCompilerGenerated() { compiler_generated(this, _) }

  /** Gets the reason this element was generated by the compiler, if any. */
  string compilerGeneratedReason() {
    exists(int i | compiler_generated(this, i) |
      i = 1 and result = "Declaring classes of adapter functions in Kotlin"
      or
      i = 2 and result = "Generated data class member"
      or
      i = 3 and result = "Default property accessor"
      or
      i = 4 and result = "Class initialisation method <clinit>"
      or
      i = 5 and result = "Enum class special member"
      or
      i = 6 and result = "Getter for a Kotlin delegated property"
      or
      i = 7 and result = "Setter for a Kotlin delegated property"
      or
      i = 8 and result = "Proxy static method for a @JvmStatic-annotated function or property"
      or
      i = 9 and result = "Forwarder for a @JvmOverloads-annotated function"
      or
      i = 10 and result = "Forwarder for Kotlin calls that need default arguments filling in"
      or
      i = 11 and result = "Forwarder for a Kotlin class inheriting an interface default method"
      or
      i = 12 and result = "Argument for enum constructor call"
      or
      i = 13 and result = "The class around a local function, a lambda, or a function reference"
    )
  }
}

/**
 * Holds if element `parent` is immediately above element `e` in the syntax tree.
 */
private predicate hasChildElement(Element parent, Element e) {
  cupackage(e, parent)
  or
  enclInReftype(e, parent) and
  not e instanceof LocalClassOrInterface
  or
  // Reasoning: any specialised instance of a local class is supposed to belong to the general
  // case of its enclosing method because we don't instantiate specialised variants of either generic
  // functions or function bodies, and therefore the local class cannot be specialised with respect
  // to its enclosing reftypes.
  e.(LocalClassOrInterface)
      .getSourceDeclaration()
      .(LocalClassOrInterface)
      .getLocalTypeDeclStmt()
      .getEnclosingCallable() = parent
  or
  not enclInReftype(e, _) and
  e.(Class).getCompilationUnit() = parent
  or
  not enclInReftype(e, _) and
  e.(Interface).getCompilationUnit() = parent
  or
  methods(e, _, _, _, parent, _)
  or
  constrs(e, _, _, _, parent, _)
  or
  params(e, _, _, parent, _)
  or
  fields(e, _, _, parent, _)
  or
  typeVars(e, _, _, _, parent)
}
